package main

import (
	"fmt"
	"unicode/utf8"
)

/*
map的key
map使用哈希表,key必须可以比较相等
除了slice,map,function的内建类型都可以作为key
Struct类型不包含上述slice,map,function字段,也可以作为key
*/
func main() {
	definedMap()

	useMap()

	//go针对中文编码问题
	chineseCodingProblem()

	fmt.Println(useCaseMap("abcabccba"))
	fmt.Println(useCaseMap("b"))
	fmt.Println(useCaseMap(""))
	fmt.Println(useCaseMap("pwwkew"))
	fmt.Println(useCaseMap("一二三二一"))

}

func chineseCodingProblem() {
	//字符串的一些操作
	// strings
	//Fields Split Join
	//Contains Index
	//ToLower ToUpper
	//Trim TrimRight TrimLeft

	s := "Hei 好好学习，天天向上"
	fmt.Println("s=", s)
	fmt.Println("转换byte数组, UTF-8编码, 每个中文占3字节, 十六进制显示b数值...")
	for _, b := range []byte(s) { //b 一个字节的数值(byte)
		fmt.Printf("%X ", b)
	}
	fmt.Println()

	// E5 A5 BD 等价于 597D, 三个b等价一个ch
	fmt.Println("分组查看每个字符, UTF-8编码, 每个中文占3字节, 十六进制显示ch数值...")
	for i, ch := range s { // ch 是 rune 类型
		fmt.Printf("(%d, %X) ", i, ch)
	}
	fmt.Println()

	//计算有几个字符
	fmt.Println("计算字符数, Rune count=", utf8.RuneCountInString(s))

	bytes := []byte(s)
	for len(bytes) > 0 {
		//ch是取出来的字符, size是字符的大小
		ch, size := utf8.DecodeRune(bytes)
		//给bytes重新截取(slice),舍弃已经取出来的字符长度
		bytes = bytes[size:]
		fmt.Printf("%c ", ch)
	}
	fmt.Println()

	//使用rune处理, 注意rune是占了4个字节(重新开辟了一块rune数组空间来存储原来的s,
	//不同于其他方法仅仅是展示方式的改变, 没有开辟新的空间)
	for i, ch := range []rune(s) {

		fmt.Printf("(%d, %c) ", i, ch)
	}
	fmt.Println()

	fmt.Println()

}

func useMap() {
	fmt.Println("traversing map...")
	//遍历
	m := map[string]string{
		"name":    "ccmouse",
		"course":  "golang",
		"site":    "imocc",
		"quality": "good",
	}
	for k, v := range m {
		fmt.Println(k, v)
	}
	fmt.Println()

	fmt.Println("getting values...")
	//对应key不存在问题
	course, ok := m["course"]
	if ok {
		fmt.Println(course)
	}
	gin, ok := m["gin"]
	if ok {
		fmt.Println(course)
	} else {
		fmt.Println("equal zero value=", gin == "")
	}
	fmt.Println()

	fmt.Println("deleting values...")
	delete(m, "name")
	fmt.Println(m)
	fmt.Println()

	fmt.Println("deleting values...")
	fmt.Println()

}

func definedMap() {
	m := map[string]string{
		"name":    "ccmouse",
		"course":  "golang",
		"site":    "imocc",
		"quality": "notbad",
	}
	fmt.Println(m)

	//m2 == empty map
	m2 := make(map[string]int)
	//m3 == nil
	var m3 map[string]int
	fmt.Println(m2, " nilFlag=", m2 == nil)
	fmt.Println(m3, " nilFlag=", m3 == nil)
	fmt.Println()
}

/*
寻找最长不含有重复字符串的子串
 abcabccba -> abc
 bbbbbb -> b
 pwwkew ->wke
*/
func useCaseMap(s string) int {
	/*
		解法:
		对于每一个字母x:
		1.lastOccurred[x]不存在,或者 < start -> 无需操作
		2.lastOccurred[x]存在,且 >= start -> 更新start
		3.更新lastOccurred[x],更新maxLength
	*/
	//记录每个字母最后出现的位置
	//这里不使用 byte, 使用rune, 那么就能支持中文字符
	lastOccurred := make(map[rune]int)
	start := 0
	maxLength := 0
	//将 string 装换为字节数组
	//这里不使用 byte, 使用rune, 那么就能支持中文字符
	//同时这里不直接 range s, 是为了避免i被赋值为占用空间,而应该被赋值为字符数
	for i, ch := range []rune(s) {
		lastI, ok := lastOccurred[ch]
		if ok && lastI >= start {
			start = lastI + 1
		}
		if i-start+1 > maxLength {
			maxLength = i - start + 1
		}
		lastOccurred[ch] = i
	}
	//fmt.Println(maxLength)
	return maxLength

}

func useCaseMap2(s string) int {
	//使用数组代替map, 0表示不存在, 存在则; BenchmarkSubstr2测试后发现, 耗时在gc上
	//每次调用都创建了一个很大的数组
	lastOccurred := make([]int, 0xffff)
	start := 0
	maxLength := 0
	for i, ch := range []rune(s) {
		lastI := lastOccurred[ch]
		if lastI >= start {
			start = lastI
		}
		if i-start+1 > maxLength {
			maxLength = i - start + 1
		}
		lastOccurred[ch] = i + 1
	}
	return maxLength
}

//只创建一次
var lastOccurred = make([]int, 0xffff)

func useCaseMap3(s string) int {
	//使用前,每次重置
	//最大的耗时在 memclrNoHeapPointers (花费在内存清理上)
	//
	for i, _ := range lastOccurred {
		lastOccurred[i] = 0
	}

	start := 0
	maxLength := 0
	for i, ch := range []rune(s) {
		lastI := lastOccurred[ch]
		if lastI >= start {
			start = lastI
		}
		if i-start+1 > maxLength {
			maxLength = i - start + 1
		}
		lastOccurred[ch] = i + 1
	}
	return maxLength
}
